/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include "board.h"
#include "drv_ospi.h"
#include "drivers/dev_spi.h"
#include "drv_config.h"

#ifdef RT_USING_OSPI

#define DRV_DEBUG
#define LOG_TAG              "drv.ospi"
#include <drv_log.h>

#if defined(BSP_USING_OSPI)

struct stm32_ospi_bus
{
    OSPI_HandleTypeDef OSPI_Handler;
    char *bus_name;
};

struct rt_spi_bus _ospi_bus1;
struct stm32_ospi_bus _stm32_ospi_bus;

static void ospi_send_cmd(struct stm32_ospi_bus *ospi_bus, struct rt_qspi_message *message)
{
    RT_ASSERT(ospi_bus != RT_NULL);
    RT_ASSERT(message != RT_NULL);

    OSPI_RegularCmdTypeDef Cmdhandler = {0};

    /* set OSPI cmd struct */
    Cmdhandler.OperationType = HAL_OSPI_OPTYPE_COMMON_CFG;
    Cmdhandler.FlashId = HAL_OSPI_FLASH_ID_1;
    Cmdhandler.Instruction = message->instruction.content;
    Cmdhandler.Address = message->address.content;
    Cmdhandler.DummyCycles = message->dummy_cycles;
    
    /* Instruction mode */
    if (message->instruction.qspi_lines == 0)
        Cmdhandler.InstructionMode = HAL_OSPI_INSTRUCTION_NONE;
    else if (message->instruction.qspi_lines == 1)
        Cmdhandler.InstructionMode = HAL_OSPI_INSTRUCTION_1_LINE;
    else if (message->instruction.qspi_lines == 2)
        Cmdhandler.InstructionMode = HAL_OSPI_INSTRUCTION_2_LINES;
    else if (message->instruction.qspi_lines == 4)
        Cmdhandler.InstructionMode = HAL_OSPI_INSTRUCTION_4_LINES;

    /* Address mode */
    if (message->address.qspi_lines == 0)
        Cmdhandler.AddressMode = HAL_OSPI_ADDRESS_NONE;
    else if (message->address.qspi_lines == 1)
        Cmdhandler.AddressMode = HAL_OSPI_ADDRESS_1_LINE;
    else if (message->address.qspi_lines == 2)
        Cmdhandler.AddressMode = HAL_OSPI_ADDRESS_2_LINES;
    else if (message->address.qspi_lines == 4)
        Cmdhandler.AddressMode = HAL_OSPI_ADDRESS_4_LINES;

    /* Address size */
    if (message->address.size == 24)
        Cmdhandler.AddressSize = HAL_OSPI_ADDRESS_24_BITS;
    else
        Cmdhandler.AddressSize = HAL_OSPI_ADDRESS_32_BITS;

    /* Data mode */
    if (message->qspi_data_lines == 0)
        Cmdhandler.DataMode = HAL_OSPI_DATA_NONE;
    else if (message->qspi_data_lines == 1)
        Cmdhandler.DataMode = HAL_OSPI_DATA_1_LINE;
    else if (message->qspi_data_lines == 2)
        Cmdhandler.DataMode = HAL_OSPI_DATA_2_LINES;
    else if (message->qspi_data_lines == 4)
        Cmdhandler.DataMode = HAL_OSPI_DATA_4_LINES;

    Cmdhandler.SIOOMode = HAL_OSPI_SIOO_INST_EVERY_CMD;
    Cmdhandler.NbData = message->parent.length;
    
    HAL_OSPI_Command(&ospi_bus->OSPI_Handler, &Cmdhandler, HAL_OSPI_TIMEOUT_DEFAULT_VALUE);
}

static rt_ssize_t ospixfer(struct rt_spi_device *device, struct rt_spi_message *message)
{
    rt_ssize_t result = 0;
    RT_ASSERT(device != RT_NULL);
    RT_ASSERT(device->bus != RT_NULL);

    struct rt_qspi_message *ospi_message = (struct rt_qspi_message *)message;
    struct stm32_ospi_bus *ospi_bus = device->bus->parent.user_data;

    const rt_uint8_t *sndb = message->send_buf;
    rt_uint8_t *rcvb = message->recv_buf;
    rt_int32_t length = message->length;

#ifdef BSP_OSPI_USING_SOFTCS
    if (message->cs_take && (device->cs_pin != PIN_NONE))
    {
        rt_pin_write(device->cs_pin, PIN_LOW);
    }
#endif

    /* send data */
    if (sndb)
    {
        ospi_send_cmd(ospi_bus, ospi_message);
        if (ospi_message->parent.length != 0)
        {
            if (HAL_OSPI_Transmit(&ospi_bus->OSPI_Handler, (rt_uint8_t *)sndb, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) == HAL_OK)
            {
                result = length;
            }
            else
            {
                LOG_E("OSPI send data failed(%d)!", ospi_bus->OSPI_Handler.ErrorCode);
                ospi_bus->OSPI_Handler.State = HAL_OSPI_STATE_READY;
                result = -RT_ERROR;
            }
        }
        else
        {
            result = 1;
        }
    }
    else if (rcvb) /* recv data */
    {
        ospi_send_cmd(ospi_bus, ospi_message);
        if (HAL_OSPI_Receive(&ospi_bus->OSPI_Handler, rcvb, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) == HAL_OK)
        {
            result = length;
        }
        else
        {
            LOG_E("OSPI recv data failed(%d)!", ospi_bus->OSPI_Handler.ErrorCode);
            ospi_bus->OSPI_Handler.State = HAL_OSPI_STATE_READY;
            result = -RT_ERROR;
        }
    }

#ifdef BSP_OSPI_USING_SOFTCS
    if (message->cs_release && (device->cs_pin != PIN_NONE))
    {
        rt_pin_write(device->cs_pin, PIN_HIGH);
    }
#endif

    return result;
}

static rt_err_t ospi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
{
    /* 硬编码配置 */
    RT_ASSERT(device != RT_NULL);
    
    struct stm32_ospi_bus *ospi_bus = device->bus->parent.user_data;
    
    /* 初始化OSPI硬件 */
    ospi_bus->OSPI_Handler.Instance = OCTOSPI1;
    ospi_bus->OSPI_Handler.Init.FifoThreshold = 4;
    ospi_bus->OSPI_Handler.Init.DualQuad = HAL_OSPI_DUALQUAD_DISABLE;
    ospi_bus->OSPI_Handler.Init.MemoryType = HAL_OSPI_MEMTYPE_MICRON;
    ospi_bus->OSPI_Handler.Init.DeviceSize = 24;
    ospi_bus->OSPI_Handler.Init.ChipSelectHighTime = 1;
    ospi_bus->OSPI_Handler.Init.FreeRunningClock = HAL_OSPI_FREERUNCLK_DISABLE;
    ospi_bus->OSPI_Handler.Init.ClockMode = HAL_OSPI_CLOCK_MODE_0;
    ospi_bus->OSPI_Handler.Init.WrapSize = HAL_OSPI_WRAP_NOT_SUPPORTED;
    ospi_bus->OSPI_Handler.Init.ClockPrescaler = 32;
    ospi_bus->OSPI_Handler.Init.SampleShifting = HAL_OSPI_SAMPLE_SHIFTING_HALFCYCLE;
    ospi_bus->OSPI_Handler.Init.DelayHoldQuarterCycle = HAL_OSPI_DHQC_DISABLE;
    ospi_bus->OSPI_Handler.Init.ChipSelectBoundary = 0;
    ospi_bus->OSPI_Handler.Init.DelayBlockBypass = HAL_OSPI_DELAY_BLOCK_BYPASSED;
    
    if (HAL_OSPI_Init(&ospi_bus->OSPI_Handler) != HAL_OK)
    {
        LOG_E("OSPI init failed!");
        return -RT_ERROR;
    }
    
    /* 配置OSPI管理器 */
    OSPIM_CfgTypeDef sOspiManagerCfg = {0};
    sOspiManagerCfg.ClkPort = 1;
    sOspiManagerCfg.NCSPort = 1;
    sOspiManagerCfg.IOLowPort = HAL_OSPIM_IOPORT_1_LOW;
    
    if (HAL_OSPIM_Config(&ospi_bus->OSPI_Handler, &sOspiManagerCfg, HAL_OSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
    {
        LOG_E("OSPIM config failed!");
        return -RT_ERROR;
    }
    
    return RT_EOK;
}

static const struct rt_spi_ops stm32_ospi_ops =
{
    .configure = ospi_configure,
    .xfer = ospixfer,
};

static int stm32_ospi_register_bus(struct stm32_ospi_bus *ospi_bus, const char *name)
{
    RT_ASSERT(ospi_bus != RT_NULL);
    RT_ASSERT(name != RT_NULL);

    _ospi_bus1.parent.user_data = ospi_bus;
    return rt_qspi_bus_register(&_ospi_bus1, name, &stm32_ospi_ops);
}

rt_err_t rt_hw_ospi_device_attach(const char *bus_name, const char *device_name, rt_base_t cs_pin, rt_uint8_t data_line_width, void (*enter_qspi_mode)(), void (*exit_qspi_mode)())
{
    struct rt_qspi_device *ospi_device = RT_NULL;
    rt_err_t result = RT_EOK;

    RT_ASSERT(bus_name != RT_NULL);
    RT_ASSERT(device_name != RT_NULL);
    RT_ASSERT(data_line_width == 1 || data_line_width == 2 || data_line_width == 4);

    ospi_device = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device));
    if (ospi_device == RT_NULL)
    {
        LOG_E("no memory, ospi bus attach device failed!");
        return -RT_ENOMEM;
    }

    ospi_device->config.qspi_dl_width = data_line_width;
    ospi_device->enter_qspi_mode = enter_qspi_mode;
    ospi_device->exit_qspi_mode = exit_qspi_mode;

#ifdef BSP_OSPI_USING_SOFTCS
    result = rt_spi_bus_attach_device_cspin(&ospi_device->parent, device_name, bus_name, cs_pin, RT_NULL);
#else
    result = rt_spi_bus_attach_device_cspin(&ospi_device->parent, device_name, bus_name, PIN_NONE, RT_NULL);
#endif /* BSP_OSPI_USING_SOFTCS */

    if (result != RT_EOK && ospi_device)
    {
        rt_free(ospi_device);
    }

    return result;
}

static int rt_hw_ospi_bus_init(void)
{
    return stm32_ospi_register_bus(&_stm32_ospi_bus, "ospi1");
}
INIT_BOARD_EXPORT(rt_hw_ospi_bus_init);

#endif /* BSP_USING_OSPI */
#endif /* RT_USING_OSPI */
